package com.geek.config.microprofile;

import com.geek.config.microprofile.source.DefaultResourceConfigSources;
import com.geek.config.microprofile.source.JavaSystemEnvironmentConfigSource;
import com.geek.config.microprofile.source.OperationSystemEnvironmentConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSource;

import java.util.*;
import java.util.function.Consumer;

public class ConfigSources implements Iterable<ConfigSource>{

  private final List<ConfigSource> configSourceList = new LinkedList<>();
  private volatile boolean addedDefaultConfigSources;
  private volatile boolean addedDiscoveredConfigSources;

  private ClassLoader classLoader;

  public ConfigSources(ClassLoader classLoader) {
    this.classLoader = classLoader;
  }

  public void addDefaultSources() {
    if (addedDefaultConfigSources) {
      return ;
    }
    addConfigSources(JavaSystemEnvironmentConfigSource.class,
            OperationSystemEnvironmentConfigSource.class,
            DefaultResourceConfigSources.class);
    addedDefaultConfigSources = true;
  }

  public void addDiscoveredSources() {
    if (addedDiscoveredConfigSources) {
      return;
    }
    ServiceLoader<DefaultResourceConfigSources> configSources = ServiceLoader.load(DefaultResourceConfigSources.class, classLoader);
    for (DefaultResourceConfigSources configSource : configSources) {
      configSourceList.add(configSource);
    }
    sort();
    addedDiscoveredConfigSources = true;
  }


  public void addConfigSources(Class<? extends ConfigSource>... configClasses) {
    for (Class aClass : configClasses) {
      configSourceList.add(newInstance(aClass));
    }
    sort();
  }

  private void sort() {
    configSourceList.sort(Comparator.comparingInt(ConfigSource::getOrdinal));
  }

  private ConfigSource newInstance(Class<? extends ConfigSource> clazz) {
    try {
      return clazz.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }


  @Override
  public Iterator<ConfigSource> iterator() {
    return configSourceList.iterator();
  }

  @Override
  public void forEach(final Consumer<? super ConfigSource> action) {
    configSourceList.forEach(action);
  }

  @Override
  public Spliterator<ConfigSource> spliterator() {
    return configSourceList.spliterator();
  }
}
